package product

import (
	"context"
	"strconv"
	"strings"

	"mall/common/ctxdatax"
	"mall/common/globalkey"
	"mall/common/moneyx"
	"mall/common/resultx"
	"mall/common/utils/pointy"
	"mall/service/forum/api/internal/logic/util"
	"mall/service/forum/api/internal/svc"
	"mall/service/forum/api/internal/types"

	// "mall/service/forum/rpc/forumclient"
	"mall/service/product/rpc/productclient"

	"github.com/zeromicro/go-zero/core/logx"
)

type GetProductByIdLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewGetProductByIdLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetProductByIdLogic {
	return &GetProductByIdLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *GetProductByIdLogic) GetProductById(req *types.ProductIdReq) (resp *types.ProductInfo, err error) {
	if req.Id == nil || *req.Id == 0 {
		return nil, resultx.NewErrCode(resultx.PRODUCT_ID_REQUIRED)
	}

	userId := ctxdatax.GetUserId(l.ctx)
	if userId == 0 {
		return nil, resultx.NewErrCode(resultx.TOKEN_EXPIRE_ERROR)
	}

	var preload string = "product:Id,ParentId,Title,Slug,Excerpt,Status"
	var eagerLoad string = "product:productDetail,productMedia,productRelationship,productTerm,productMeta,productAttribute,productVariation,productVariationMeta"
	preload += ";productDetail:ContentHtml"
	preload += ";productMedia:Id,Uid,Url,Thumbnail,Order"
	preload += ";productRelationship:PerfumeId,PerfumeName,PerfumeSlug,BrandId,BrandName,BrandSlug"
	preload += ";productTerm:Id,Name,Slug,ParentId"
	preload += ";productMeta:mediaId,mediaUrl,showStock,stockUnit,reviewCount,saleCount,averageRating,gemPriceMin,gemPriceMax,retailPriceMin,retailPriceMax,stock"
	preload += ";productAttribute:Id,IsSku,Name,Slug,HasImage"
	preload += ";productVariation:Id,GemPrice,RetailPrice,CounterPrice,Weight"
	preload += ";productVariationMeta:stock,mediaId,mediaUrl,sku,skuKey,saleCount,totalSaleCount"
	var platformId uint32 = globalkey.ThirdPartyPlatformWechatMini

	var searchLogId *uint64
	if req.SearchId != nil && *req.SearchId > 0 {
		searchLogId = pointy.GetPointer(uint64(*req.SearchId))
	}

	res, err := l.svcCtx.ProductRpc.GetProductById(l.ctx, &productclient.BaseIdReq{
		Id:          req.Id,
		Preload:     pointy.GetPointer(preload),
		EagerLoad:   pointy.GetPointer(eagerLoad),
		VisitorId:   pointy.GetPointer(userId),
		PlatformId:  pointy.GetPointer(platformId),
		SearchLogId: searchLogId,
	})
	if err != nil {
		return nil, err
	}

	if res == nil || res.Id == nil || *res.Id == 0 {
		return nil, resultx.NewErrCode(resultx.PRODUCT_ID_INVALID)
	}

	var categories []*types.CategoryInfo
	if len(res.Categories) > 0 {
		for _, v := range res.Categories {
			categories = append(categories, &types.CategoryInfo{
				Id:       v.Id,
				Name:     v.Name,
				Slug:     v.Slug,
				ParentId: v.ParentId,
			})
		}
	}
	var tags []*types.TagInfo
	if len(res.Tags) > 0 {
		for _, v := range res.Tags {
			tags = append(tags, &types.TagInfo{
				Id:   v.Id,
				Name: v.Name,
				Slug: v.Slug,
			})
		}
	}

	var sellPoint *string
	var gemPriceMin *string
	var gemPriceMax *string
	var retailPriceMin *string
	var retailPriceMax *string
	var stock *uint32
	var stockUnit *string
	var showStock *bool
	var isNew *bool
	var isHot *bool
	var saleCount *uint32
	var reviewCount *uint32
	var ratingCount *uint32
	var averageRating *string
	var meta []*types.MetaInfo
	if len(res.Meta) > 0 {
		for _, v := range res.Meta {
			if v.Key == nil || v.Value == nil {
				continue
			}

			val := *v.Value
			switch *v.Key {
			case "gemPriceMin":
				gemPriceMin = pointy.GetPointer(val)
			case "gemPriceMax":
				gemPriceMax = pointy.GetPointer(val)
			case "retailPriceMin":
				retailPriceMin = pointy.GetPointer(val)
			case "retailPriceMax":
				retailPriceMax = pointy.GetPointer(val)
			case "saleCount":
				if i, err := strconv.ParseInt(val, 10, 32); err == nil {
					saleCount = pointy.GetPointer(uint32(i))
				}
			case "reviewCount":
				if i, err := strconv.ParseInt(val, 10, 32); err == nil {
					reviewCount = pointy.GetPointer(uint32(i))
				}
			case "ratingCount":
				if i, err := strconv.ParseInt(val, 10, 32); err == nil {
					ratingCount = pointy.GetPointer(uint32(i))
				}
			case "averageRating":
				if f, err := strconv.ParseFloat(val, 64); err == nil {
					averageRating = pointy.GetPointer(moneyx.FormatStar(f, 1))
				}
			case "isNew":
				if val == "1" {
					isNew = pointy.GetPointer(true)
				}
			case "isHot":
				if val == "1" {
					isHot = pointy.GetPointer(true)
				}
			case "stock":
				if i, err := strconv.ParseInt(val, 10, 32); err == nil {
					stock = pointy.GetPointer(uint32(i))
				}
			default:
				meta = append(meta, &types.MetaInfo{
					Id:    v.Id,
					Key:   v.Key,
					Value: v.Value,
				})
			}
		}
	}

	var brand *types.KbEntryInfo
	var perfume *types.KbEntryInfo
	if len(res.Relations) > 0 {
		for _, v := range res.Relations {
			if v.Key != nil {
				switch *v.Key {
				case globalkey.ProductRelationNameBrand:
					brand = &types.KbEntryInfo{
						Id:    v.Id,
						Title: v.Name,
						Slug:  v.Slug,
					}
				case globalkey.ProductRelationNamePerfume:
					perfume = &types.KbEntryInfo{
						Id:    v.Id,
						Title: v.Name,
						Slug:  v.Slug,
					}
				}
			}
		}
	}

	var photos []*types.MediaInfo
	if len(res.Media) > 0 {
		for _, v := range res.Media {
			var url string
			if v.Url != nil && len(*v.Url) > 0 {
				url = *v.Url
			}
			if len(url) == 0 && v.Uri != nil && len(*v.Uri) > 0 {
				url = *v.Uri
			}

			var thumbnail *bool
			if v.Thumbnail != nil {
				thumbnail = pointy.GetPointer(*v.Thumbnail > 0)
			}

			photos = append(photos, &types.MediaInfo{
				Uid:       v.Uid,
				Id:        v.Id,
				Thumbnail: thumbnail,
				Order:     v.Order,
				Url:       pointy.GetPointer(url),
				AltText:   v.AltText,
				MetaData:  v.MetaData,
			})
		}
	}

	var attrs []*types.ProductAttrInfo
	if len(res.Attrs) > 0 {
		for _, v := range res.Attrs {
			if len(v.Options) > 0 {
				var options []*types.ProductAttrOptInfo
				for _, opt := range v.Options {
					options = append(options, &types.ProductAttrOptInfo{
						Id:       opt.Id,
						Label:    opt.Label,
						Slug:     opt.Slug,
						MediaId:  opt.MediaId,
						MediaUrl: opt.MediaUrl,
					})
				}
				var hasMedia *bool
				if v.HasMedia != nil && *v.HasMedia > 0 {
					hasMedia = pointy.GetPointer(true)
				}
				attrs = append(attrs, &types.ProductAttrInfo{
					Id:       v.Id,
					Label:    v.Label,
					Slug:     v.Slug,
					HasMedia: hasMedia,
					Options:  options,
				})
			}
		}
	}

	var specs []*types.ProductAttrInfo
	idSpecOptMap := make(map[string]*types.ProductAttrOptInfo)
	idSpecMap := make(map[string]*productclient.ProductAttrInfo)
	if len(res.Specs) > 0 {
		for _, v := range res.Specs {
			if len(v.Options) > 0 {
				var options []*types.ProductAttrOptInfo
				for _, opt := range v.Options {
					if opt.Id == nil {
						continue
					}

					optId := strconv.FormatInt(int64(*opt.Id), 10)
					option := &types.ProductAttrOptInfo{
						Id:       opt.Id,
						Label:    opt.Label,
						Slug:     opt.Slug,
						MediaId:  opt.MediaId,
						MediaUrl: opt.MediaUrl,
					}
					idSpecOptMap[optId] = option
					options = append(options, option)
					idSpecMap[optId] = v
				}
				var hasMedia *bool
				if v.HasMedia != nil && *v.HasMedia > 0 {
					hasMedia = pointy.GetPointer(true)
				}
				specs = append(specs, &types.ProductAttrInfo{
					Id:       v.Id,
					Label:    v.Label,
					Slug:     v.Slug,
					HasMedia: hasMedia,
					Options:  options,
				})
			}
		}
	}

	var skus []*types.ProductVariationInfo
	if len(res.Skus) > 0 {
		for _, v := range res.Skus {
			if v.Key == nil {
				continue
			}

			var optKeys []string
			var specs []*types.ProductAttrInfo
			if keys := strings.Split(*v.Key, ","); len(keys) > 0 {
				for _, optKey := range keys {
					if spec, ok := idSpecMap[optKey]; ok {
						if opt, ok := idSpecOptMap[optKey]; ok {
							optKey = "opt_" + optKey
							optKeys = append(optKeys, optKey)
							opt.Key = pointy.GetPointer(optKey)
							options := []*types.ProductAttrOptInfo{opt}
							var hasMedia *bool
							if spec.HasMedia != nil && *spec.HasMedia > 0 {
								hasMedia = pointy.GetPointer(true)
							}
							var specKey *string
							if spec.Id != nil {
								specKey = pointy.GetPointer("spec_" + strconv.FormatInt(int64(*spec.Id), 10))
							}
							specs = append(specs, &types.ProductAttrInfo{
								Id:       spec.Id,
								Key:      specKey,
								Label:    spec.Label,
								Slug:     spec.Slug,
								HasMedia: hasMedia,
								Options:  options,
							})
						}
					}
				}
			}
			skus = append(skus, &types.ProductVariationInfo{
				Id:           v.Id,
				Key:          pointy.GetPointer(strings.Join(optKeys, "_-_")),
				GemPrice:     v.GemPrice,
				RetailPrice:  v.RetailPrice,
				CounterPrice: v.CounterPrice,
				Inventory:    v.Inventory,
				Weight:       v.Weight,
				SaleCount:    v.SaleCount,
				// MediaId:      v.MediaId,
				MediaUrl: v.MediaUrl,
				Specs:    specs,
			})
		}
	}

	var statusText *string
	if res.Status != nil {
		statusText = pointy.GetPointer(util.GetStatusText(int32(*res.Status)))
	}

	return &types.ProductInfo{
		Id:             res.Id,
		Title:          res.Title,
		Slug:           res.Slug,
		Excerpt:        res.Excerpt,
		ContentHtml:    res.Content,
		Status:         res.Status,
		StatusText:     statusText,
		Brand:          brand,
		Perfume:        perfume,
		Categories:     categories,
		Tags:           tags,
		Media:          photos,
		Meta:           meta,
		Attrs:          attrs,
		Specs:          specs,
		Skus:           skus,
		SellPoint:      sellPoint,
		GemPriceMin:    gemPriceMin,
		GemPriceMax:    gemPriceMax,
		RetailPriceMin: retailPriceMin,
		RetailPriceMax: retailPriceMax,
		Stock:          stock,
		StockUnit:      stockUnit,
		ShowStock:      showStock,
		IsNew:          isNew,
		IsHot:          isHot,
		SaleCount:      saleCount,
		ReviewCount:    reviewCount,
		RatingCount:    ratingCount,
		AverageRating:  averageRating,
	}, nil
}
